package com.peas.platform.module.icims.budget.adjust.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.peas.platform.common.core.enums.BaseEnum;
import com.peas.platform.framework.mybatisplus.domain.BaseDomain;
import com.peas.platform.framework.mybatisplus.po.BasePo;
import com.peas.platform.framework.mybatisplus.service.BaseServiceImpl;
import com.peas.platform.module.icims.base.approve.component.ApproveCallback;
import com.peas.platform.module.icims.base.approve.dto.ApproveDto;
import com.peas.platform.module.icims.base.approve.dto.ApproveOrderNodeDto;
import com.peas.platform.module.icims.base.approve.enums.ApproveOrderBusinessType;
import com.peas.platform.module.icims.base.approve.service.ApproveOrderService;
import com.peas.platform.module.icims.base.approve.service.ApproveService;
import com.peas.platform.module.icims.base.mdm.dto.GovernmentDto;
import com.peas.platform.module.icims.base.mdm.service.GovernmentService;
import com.peas.platform.module.icims.budget.adjust.api.req.SaveBudgetNewVo;
import com.peas.platform.module.icims.budget.adjust.api.req.SubmitBudgetNewVo;
import com.peas.platform.module.icims.budget.adjust.controller.vo.*;
import com.peas.platform.module.icims.budget.adjust.domain.BudgetNewDomain;
import com.peas.platform.module.icims.budget.adjust.dto.*;
import com.peas.platform.module.icims.budget.adjust.service.*;
import com.peas.platform.module.icims.budget.apply.enums.BudgetState;
import com.peas.platform.module.icims.budget.archives.dto.BudgetKpiDto;
import com.peas.platform.module.icims.budget.archives.enums.KpiSource;
import com.peas.platform.module.icims.budget.archives.service.BudgetKpiService;
import com.peas.platform.module.icims.budget.project.dto.BudgetProjectTwoDto;
import com.peas.platform.module.icims.budget.project.service.BudgetProjectTwoService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

@Service
public class BudgetNewServiceImpl extends BaseServiceImpl<BudgetNewDto> implements BudgetNewService, ApproveCallback {

    @Resource
    private BudgetNewDomain budgetNewDomain;
    @Resource
    private BudgetProjectTwoService budgetProjectTwoService;
    @Resource
    private BudgetNewPurchaseService budgetNewPurchaseService;
    @Resource
    private BudgetNewItemService budgetNewItemService;
    @Resource
    private BudgetNewYearService budgetNewYearService;
    @Resource
    private BudgetNewKpiService budgetNewKpiService;
    @Resource
    private BudgetNewFileService budgetNewFileService;
    @Resource
    private ApproveService approveService;
    @Resource
    private ApproveOrderService approveOrderService;
    @Resource
    private GovernmentService governmentService;
    @Resource
    private BudgetKpiService budgetKpiService;

    @Override
    public BaseDomain<BudgetNewDto, ? extends BasePo> getDomain() {
        return budgetNewDomain;
    }

    private BudgetNewDto getOneByProjectTwoId(Long projectTwoId) {
        Assert.notNull(projectTwoId, "二级项目ID不能为空");
        BudgetNewDto c = new BudgetNewDto();
        c.setProjectTwoId(projectTwoId);
        return this.getOne(c, false);
    }

    @Transactional
    @Override
    public BudgetNewDto saveBudgetNew(SaveBudgetNewVo vo) {
        BudgetNewDto bod = BeanUtil.copyProperties(vo, BudgetNewDto.class);
        if (StrUtil.isBlank(bod.getState())) {
            bod.setState(BudgetState.save.getKey());
        }
        Assert.notNull(vo.getProjectTwoId(), "请选择二级项目");
        BudgetProjectTwoDto projectTwoDto = budgetProjectTwoService.getById(vo.getProjectTwoId());
        Assert.notNull(projectTwoDto, "二级项目不存在");
        vo.setApplyOneFund(null);
        vo.setApproveOneFund(null);
        int result;
        if (vo.getId() != null) {
            BudgetNewDto bean = this.getById(vo.getId());
            BudgetState state = BaseEnum.key2Enum(BudgetState.class, bean.getState());
            Assert.isTrue(BudgetState.save == state || BudgetState.revocation == state || BudgetState.reject == state, "一上预算编制[" + bean.getCode() + "]的状态为[" + state.getKey() + "-" + state.getValue() + "]，不允许修改");
            result = this.updateById(bod);
        } else {
            BudgetNewDto bean = this.getOneByProjectTwoId(vo.getProjectTwoId());
            if (bean != null) {
                BudgetState state = BaseEnum.key2Enum(BudgetState.class, bean.getState());
                Assert.isTrue(BudgetState.save == state || BudgetState.revocation == state || BudgetState.reject == state, "一上预算编制[" + bean.getCode() + "]的状态为[" + state.getKey() + "-" + state.getValue() + "]，不允许修改");
                bod.setId(bean.getId());
                bod.setVersion(bean.getVersion());
                result = this.updateById(bod);
            } else {
                result = this.save(bod);
            }
        }
        Assert.isTrue(result > 0, "保存失败");
        List<BudgetNewPurchaseDto> bopds = budgetNewPurchaseService.listByField("budgetNewId", bod.getId());
        budgetNewPurchaseService.removeBatchByList(bopds);
        List<BudgetNewItemDto> boids = budgetNewItemService.listByField("budgetNewId", bod.getId());
        budgetNewItemService.removeBatchByList(boids);
        List<BudgetNewYearDto> boyds = budgetNewYearService.listByField("budgetNewId", bod.getId());
        budgetNewYearService.removeBatchByList(boyds);
        List<BudgetNewKpiDto> bokds = budgetNewKpiService.listByField("budgetNewId", bod.getId());
        budgetNewKpiService.removeBatchByList(bokds);
        List<BudgetNewFileDto> bofds = budgetNewFileService.listByField("budgetNewId", bod.getId());
        budgetNewFileService.removeBatchByList(bofds);
        // purchases
        if (CollUtil.isNotEmpty(vo.getPurchases())) {
            List<BudgetNewPurchaseDto> bopdList = new ArrayList<>();
            CollUtil.forEach(vo.getPurchases(), (CollUtil.Consumer<BudgetNewPurchaseVo>) (value, index) -> {
                BudgetNewPurchaseDto dto = BeanUtil.copyProperties(value, BudgetNewPurchaseDto.class);
                dto.setBudgetNewId(bod.getId());
                bopdList.add(dto);
            });
            budgetNewPurchaseService.saveBatch(bopdList);
        }
        // items
        if (CollUtil.isNotEmpty(vo.getItems())) {
            List<BudgetNewItemDto> boidList = new ArrayList<>();
            CollUtil.forEach(vo.getItems(), (CollUtil.Consumer<BudgetNewItemVo>) (value, index) -> {
                BudgetNewItemDto dto = BeanUtil.copyProperties(value, BudgetNewItemDto.class);
                dto.setBudgetNewId(bod.getId());
                boidList.add(dto);
            });
            budgetNewItemService.saveBatch(boidList);
        }
        // years
        if (CollUtil.isNotEmpty(vo.getYears())) {
            List<BudgetNewYearDto> boydList = new ArrayList<>();
            CollUtil.forEach(vo.getYears(), (CollUtil.Consumer<BudgetNewYearVo>) (value, index) -> {
                BudgetNewYearDto dto = BeanUtil.copyProperties(value, BudgetNewYearDto.class);
                dto.setBudgetNewId(bod.getId());
                boydList.add(dto);
            });
            budgetNewYearService.saveBatch(boydList);
        }
        // kpis
        List<BudgetNewKpiVo> kpis = vo.getKpis();
        if (CollUtil.isNotEmpty(kpis)) {
            CollUtil.forEach(kpis, (CollUtil.Consumer<BudgetNewKpiVo>) (one, i1) -> {
                if (one.getParentId() == null) {
                    BudgetNewKpiDto dto1 = BeanUtil.copyProperties(one, BudgetNewKpiDto.class);
                    dto1.setBudgetNewId(bod.getId());
                    budgetNewKpiService.save(dto1);
                    CollUtil.forEach(kpis, (CollUtil.Consumer<BudgetNewKpiVo>) (two, i2) -> {
                        if (two.getParentId() != null && two.getParentId().compareTo(one.getId()) == 0) {
                            BudgetNewKpiDto dto2 = BeanUtil.copyProperties(two, BudgetNewKpiDto.class);
                            dto2.setBudgetNewId(bod.getId());
                            dto2.setParentId(dto1.getId());
                            budgetNewKpiService.save(dto2);
                            CollUtil.forEach(kpis, (CollUtil.Consumer<BudgetNewKpiVo>) (three, i3) -> {
                                if (three.getParentId() != null && three.getParentId().compareTo(two.getId()) == 0) {
                                    BudgetNewKpiDto dto3 = BeanUtil.copyProperties(three, BudgetNewKpiDto.class);
                                    dto3.setBudgetNewId(bod.getId());
                                    dto3.setParentId(dto2.getId());
                                    if (StrUtil.isBlank(dto3.getKpiCode())) {
                                        BudgetKpiDto kpi = budgetKpiService.addBudgetKpi(KpiSource.budgetNew, dto3.getKpiName(), two.getId(), dto3.getWeight());
                                        if (kpi != null)
                                            dto3.setKpiCode(kpi.getCode());
                                    }
                                    budgetNewKpiService.save(dto3);
                                }
                            });
                        }
                    });
                }
            });
        }
        // files
        if (CollUtil.isNotEmpty(vo.getFiles())) {
            List<BudgetNewFileDto> boydList = new ArrayList<>();
            CollUtil.forEach(vo.getFiles(), (CollUtil.Consumer<BudgetNewFileVo>) (value, index) -> {
                BudgetNewFileDto dto = BeanUtil.copyProperties(value, BudgetNewFileDto.class);
                dto.setBudgetNewId(bod.getId());
                boydList.add(dto);
            });
            budgetNewFileService.saveBatch(boydList);
        }
        return bod;
    }

    @Transactional
    @Override
    public void submitBudgetNew(SubmitBudgetNewVo vo) {
        Assert.notNull(vo.getApproveId(), "请选择审批模版");
        SaveBudgetNewVo v = BeanUtil.copyProperties(vo, SaveBudgetNewVo.class);
        v.setState(BudgetState.submit.getKey());
        v.setItems(vo.getItems());
        v.setKpis(vo.getKpis());
        BudgetNewDto bod = this.saveBudgetNew(v);
        ApproveDto ad = approveService.getById(vo.getApproveId());
        approveOrderService.createApproveOrder(ad, BeanUtil.copyToList(vo.getApproveNodes(), ApproveOrderNodeDto.class), ApproveOrderBusinessType.budgetNew, bod.getId(), bod.getCode(), null, this.getClass());
    }

    @Transactional
    @Override
    public void revocationBudgetNew(Long id) {
        BudgetNewDto dto = this.getById(id);
        Assert.notNull(dto, "找不到预算编制");
        dto.setState(BudgetState.revocation.getKey());
        this.updateById(dto);
        approveOrderService.revocationApproveOrder(ApproveOrderBusinessType.budgetNew, id);
    }

    @Override
    public List<BudgetNewDto> queryBudgetNewById(List<Long> idList) {
        List<BudgetNewDto> list = this.listByIds(idList);
        CollUtil.forEach(list, (CollUtil.Consumer<BudgetNewDto>) (value, index) -> calcBudgetNewTotalAmount(value));
        return list;
    }

    @Override
    public List<BudgetNewDto> queryBudgetNewByOrganizationId(Long organizationId) {
        //TODO
        return null;
    }

    @Override
    public List<BudgetNewDto> queryBudgetNewByProjectOneId(Long projectOneId) {
        List<BudgetNewDto> list = new ArrayList<>();
        List<BudgetProjectTwoDto> ptds = budgetProjectTwoService.listByField("projectOneId", projectOneId);
        CollUtil.forEach(ptds, (CollUtil.Consumer<BudgetProjectTwoDto>) (v1, i1) -> {
            List<BudgetNewDto> bods = listByField("projectTwoId", v1.getId());
            CollUtil.forEach(bods, (CollUtil.Consumer<BudgetNewDto>) (v2, i2) -> {
                calcBudgetNewTotalAmount(v2);
                list.add(v2);
            });
        });
        return list;
    }

    @Override
    public List<BudgetNewDto> queryBudgetNewByApplyGovernmentId(Long applyGovernmentId) {
        List<BudgetNewDto> list = new ArrayList<>();
        List<GovernmentDto> gds = governmentService.queryGovernmentAndSubs(applyGovernmentId);
        CollUtil.forEach(gds, (CollUtil.Consumer<GovernmentDto>) (v1, i1) -> {
            List<BudgetNewDto> bods = listByField("applyGovId", v1.getId());
            CollUtil.forEach(bods, (CollUtil.Consumer<BudgetNewDto>) (v2, i2) -> {
                calcBudgetNewTotalAmount(v2);
                list.add(v2);
            });
        });
        return list;
    }

    @Override
    public List<BudgetNewDto> queryBudgetNewByUseGovernmentId(Long useGovernmentId) {
        List<BudgetNewDto> list = new ArrayList<>();
        List<GovernmentDto> gds = governmentService.queryGovernmentAndSubs(useGovernmentId);
        CollUtil.forEach(gds, (CollUtil.Consumer<GovernmentDto>) (v1, i1) -> {
            List<BudgetNewDto> bods = listByField("useGovId", v1.getId());
            CollUtil.forEach(bods, (CollUtil.Consumer<BudgetNewDto>) (v2, i2) -> {
                calcBudgetNewTotalAmount(v2);
                list.add(v2);
            });
        });
        return list;
    }

    @Override
    public BigDecimal calcBudgetNewTotalAmount(BudgetNewDto dto) {
        if (dto == null) {
            return BigDecimal.ZERO;
        }
        AtomicReference<BigDecimal> all = new AtomicReference<>(BigDecimal.ZERO);
        List<BudgetNewItemDto> boids = budgetNewItemService.listByField("budgetNewId", dto.getId());
        CollUtil.forEach(boids, (CollUtil.Consumer<BudgetNewItemDto>) (boid, index) -> {
            BigDecimal sum = all.get().add(boid.getPrice().multiply(boid.getNum()));
            all.set(sum);
        });
        dto.setTotalAmount(all.get());
        return all.get();
    }

    @Transactional
    @Override
    public void callbackAfterApprove(Long businessId, boolean isPass, boolean isFinalPass) {
        if (businessId == null)
            return;
        if (isPass) {
            if (isFinalPass) {
                BudgetNewDto bod = this.getById(businessId);
                bod.setState(BudgetState.pass.getKey());
                this.updateById(bod);
            }
        } else {
            BudgetNewDto bod = this.getById(businessId);
            bod.setState(BudgetState.reject.getKey());
            this.updateById(bod);
        }
    }

}
